home *** CD-ROM | disk | FTP | other *** search
- /*****************************************************************************
- * "Irit" - the 3d (not only polygonal) solid modeller. *
- * *
- * Written by: Gershon Elber Ver 0.2, Mar. 1990 *
- ******************************************************************************
- * Module to handle the high level Boolean operations. The other modules *
- * should only call this module to perform Boolean operations. All the *
- * operations are none-destructives, meaning the given data is not modified. *
- * Note all the polygons of the two given objects must be convex, and the *
- * returned object will also have only convex polygons! *
- *****************************************************************************/
-
- /* #define DEBUG2 If defined, defines some printing routines. */
-
- #include <stdio.h>
- #include <ctype.h>
- #include <math.h>
- #include <string.h>
- #include <signal.h>
- #include "program.h"
- #include "allocate.h"
- #include "attribut.h"
- #include "booleang.h"
- #include "booleanl.h"
- #include "convex.h"
- #include "poly_cln.h"
- #include "ctrl-brk.h"
- #include "geomat3d.h"
- #include "objects.h"
- #include "windows.h"
-
- int BooleanOutputInterCurve = FALSE; /* Kind of output from Boolean oper. */
-
- static jmp_buf LclLongJumpBuffer; /* Used in fatal Boolean error. */
- static int FatalErrorType, /* Type of fatal Boolean error. */
- BooleanOperation; /* One of BooleanOR, BooleanAND, etc. */
-
- static IPObjectStruct *BooleanCombineThreeObjs(IPObjectStruct *PObj1,
- IPObjectStruct *PObj2,
- IPObjectStruct *PObj3);
- static IPObjectStruct *BooleanCoplanar(IPObjectStruct *PObj1,
- IPObjectStruct *PObj2, int BoolOper);
- static IPObjectStruct *VerifyBooleanInput(IPObjectStruct *PObj1,
- IPObjectStruct *PObj2, int Oper);
- static IPPolygonStruct *ComputeRotatedPolys(IPPolygonStruct *Pl, int CopyPl,
- MatrixType RotMat);
-
- static void BooleanFPE(int Type);
- static void SetBooleanOutputKind(void);
-
- #ifdef DEBUG2
- static void PrintVrtxList(IPVertexStruct *V);
- #endif /* DEBUG2 */
-
- /*****************************************************************************
- * Verify input for Booleans. Ret. NULL if OK, otherwise an obj to return. *
- *****************************************************************************/
- static IPObjectStruct *VerifyBooleanInput(IPObjectStruct *PObj1,
- IPObjectStruct *PObj2, int Oper)
- {
- IPObjectStruct *PObj;
- IPPolygonStruct *Pl;
-
- BooleanOperation = Oper;
-
- SetBooleanOutputKind();
-
- if (!IP_IS_POLY_OBJ(PObj1) || (PObj2 != NULL && !IP_IS_POLY_OBJ(PObj2)))
- IritFatalError("Boolean: operation on non polygonal object(s).");
-
- signal(SIGFPE, BooleanFPE); /* Will trap floating point errors. */
-
- switch (Oper) {
- case BOOL_OPER_OR:
- if (IP_IS_POLYLINE_OBJ(PObj1) && IP_IS_POLYLINE_OBJ(PObj2)) {
- if (PObj1 -> U.Pl == NULL)
- PObj = CopyObject(NULL, PObj2, FALSE);
- else {
- PObj = CopyObject(NULL, PObj1, FALSE);
- Pl = IritPrsrGetLastPoly(PObj -> U.Pl);
- Pl -> Pnext = CopyPolygonList(PObj2 -> U.Pl);
- }
- return PObj;
- }
- case BOOL_OPER_AND:
- case BOOL_OPER_SUB:
- case BOOL_OPER_CUT:
- case BOOL_OPER_MERGE:
- case BOOL_OPER_NEG:
- if (IP_IS_POLYLINE_OBJ(PObj1) ||
- (PObj2 != NULL && IP_IS_POLYLINE_OBJ(PObj2))) {
- WndwInputWindowPutStr(
- "Boolean: illegal operation on mixed polygon/line geometric object(s).");
- PObj = GenPolyObject("", NULL, NULL);
- return PObj;
- }
-
- if (Oper != BOOL_OPER_NEG) {
- ConvexPolyObject(PObj1);/* Make sure all polygons are convex.*/
- ConvexPolyObject(PObj2);
- }
-
- return NULL;
- default:
- IritFatalError("Boolean: undefined Boolean operation.");
- return NULL; /* Make warning silent. */
- }
- }
-
- /*****************************************************************************
- * Perform a Boolean OR between two objects. *
- *****************************************************************************/
- IPObjectStruct *BooleanOR(IPObjectStruct *PObj1, IPObjectStruct *PObj2)
- {
- IPObjectStruct *PObj;
- IPPolygonStruct *Pl;
-
- if ((PObj = VerifyBooleanInput(PObj1, PObj2, BOOL_OPER_OR)) != NULL)
- return PObj;
- else {
- if (setjmp(LclLongJumpBuffer) == 0) { /* Its the setjmp itself call! */
- signal(SIGFPE, BooleanFPE); /* Will trap floating point errors. */
- if (BooleanOutputInterCurve)
- PObj = BooleanLow1Out2(PObj1, PObj2);/* Ret intersection crv.*/
- else
- PObj = BooleanCombineThreeObjs(BooleanLow1Out2(PObj1, PObj2),
- BooleanLow1Out2(PObj2, PObj1),
- BooleanCoplanar(PObj1, PObj2,
- BOOL_OPER_OR));
-
- }
- else {
- /* We gain control from fatal error long jump - usually we should*/
- /* return empty object, but if error is no intersection between */
- /* the two objects, we assume they have no common volume and */
- /* return a new object consists of the concat. of all polygons! */
- if (FatalErrorType != FTL_BOOL_NO_INTER) {
- PObj = GenPolyObject("", NULL, NULL);/* Return empty object. */
- }
- else {
- if (PObj1 -> U.Pl == NULL)
- PObj = CopyObject(NULL, PObj2, FALSE);
- else {
- PObj = CopyObject(NULL, PObj1, FALSE);/* Copy Obj1 polys.*/
- Pl = PObj -> U.Pl;
- while (Pl -> Pnext) Pl = Pl -> Pnext;
- Pl -> Pnext = CopyPolygonList(PObj2 -> U.Pl);/*Obj2 poly.*/
- }
- }
- }
- }
-
- signal(SIGFPE, DefaultFPEHandler); /* Default FPE trapping. */
-
- AttrSetObjectColor(PObj, BooleanOutputInterCurve ?
- GlblICrvColor :
- GlblBoolColor); /* Default bool object color. */
-
- return PObj;
- }
-
- /*****************************************************************************
- * Perform a Boolean AND between two objects. *
- *****************************************************************************/
- IPObjectStruct *BooleanAND(IPObjectStruct *PObj1, IPObjectStruct *PObj2)
- {
- IPObjectStruct *PObj;
-
- if ((PObj = VerifyBooleanInput(PObj1, PObj2, BOOL_OPER_AND)) != NULL)
- return PObj;
- else {
- if (setjmp(LclLongJumpBuffer) == 0) { /* Its the setjmp itself call! */
- signal(SIGFPE, BooleanFPE); /* Will trap floating point errors. */
- if (BooleanOutputInterCurve)
- PObj = BooleanLow1In2(PObj1, PObj2);/* Ret intersection crv. */
- else
- PObj = BooleanCombineThreeObjs(BooleanLow1In2(PObj1, PObj2),
- BooleanLow1In2(PObj2, PObj1),
- BooleanCoplanar(PObj1, PObj2,
- BOOL_OPER_AND));
-
- }
- else {/* We gain control from fatal error long jump - ret empty obj. */
- PObj = GenPolyObject("", NULL, NULL);
- }
- }
-
- signal(SIGFPE, DefaultFPEHandler); /* Default FPE trapping. */
-
- AttrSetObjectColor(PObj, BooleanOutputInterCurve ?
- GlblICrvColor :
- GlblBoolColor); /* Default bool object color. */
-
- return PObj;
- }
-
- /*****************************************************************************
- * Perform a Boolean SUBSTRACT between two objects (PObj1 - PObj2). *
- *****************************************************************************/
- IPObjectStruct *BooleanSUB(IPObjectStruct *PObj1, IPObjectStruct *PObj2)
- {
- IPObjectStruct *PObj, *PTemp, *PTempRev;
-
- if ((PObj = VerifyBooleanInput(PObj1, PObj2, BOOL_OPER_SUB)) != NULL)
- return PObj;
- else {
- if (setjmp(LclLongJumpBuffer) == 0) { /* Its the setjmp itself call! */
- signal(SIGFPE, BooleanFPE); /* Will trap floating point errors. */
- /* The 1 in 2 must be reversed (the inside/outside orientation): */
- if (BooleanOutputInterCurve) {
- PObj = BooleanLow1In2(PObj2, PObj1);/* Ret intersection crv. */
- }
- else {
- PTemp = BooleanLow1In2(PObj2, PObj1);
- PTempRev = BooleanNEG(PTemp);
- IPFreeObject(PTemp);
-
- PObj = BooleanCombineThreeObjs(BooleanLow1Out2(PObj1, PObj2),
- PTempRev,
- BooleanCoplanar(PObj1, PObj2,
- BOOL_OPER_SUB));
- }
- }
- else {/* We gain control from fatal error long jump - ret empty obj. */
- PObj = GenPolyObject("", NULL, NULL);
- }
- }
-
- signal(SIGFPE, DefaultFPEHandler); /* Default FPE trapping. */
-
- AttrSetObjectColor(PObj, BooleanOutputInterCurve ?
- GlblICrvColor :
- GlblBoolColor); /* Default bool object color. */
-
- return PObj;
- }
-
- /*****************************************************************************
- * Perform a Boolean CUT between two objects (PObj1 / PObj2). *
- *****************************************************************************/
- IPObjectStruct *BooleanCUT(IPObjectStruct *PObj1, IPObjectStruct *PObj2)
- {
- IPObjectStruct *PObj;
-
- if ((PObj = VerifyBooleanInput(PObj1, PObj2, BOOL_OPER_CUT)) != NULL)
- return PObj;
- else {
- if (setjmp(LclLongJumpBuffer) == 0) { /* Its the setjmp itself call! */
- signal(SIGFPE, BooleanFPE); /* Will trap floating point errors. */
- /* The 1 in 2 must be reversed (the inside/outside orientation): */
- if (BooleanOutputInterCurve) {
- PObj = BooleanLow1In2(PObj2, PObj1);/* Ret intersection crv. */
- }
- else {
- PObj = BooleanLow1Out2(PObj1, PObj2);
- }
- }
- else {/* We gain control from fatal error long jump - ret empty obj. */
- PObj = GenPolyObject("", NULL, NULL);
- }
- }
-
- signal(SIGFPE, DefaultFPEHandler); /* Default FPE trapping. */
-
- AttrSetObjectColor(PObj, BooleanOutputInterCurve ?
- GlblICrvColor :
- GlblBoolColor); /* Default bool object color. */
-
- return PObj;
- }
-
- /*****************************************************************************
- * Perform a Boolean MERGE between two objects. *
- *****************************************************************************/
- IPObjectStruct *BooleanMERGE(IPObjectStruct *PObj1, IPObjectStruct *PObj2)
- {
- IPObjectStruct *PObj;
- IPPolygonStruct *Pl;
-
- if ((PObj = VerifyBooleanInput(PObj1, PObj2, BOOL_OPER_MERGE)) != NULL)
- return PObj;
- else {
- if (PObj1 -> U.Pl == NULL)
- PObj = CopyObject(NULL, PObj2, FALSE);
- else {
- PObj = CopyObject(NULL, PObj1, FALSE); /* Copy Obj1 polys. */
- Pl = PObj -> U.Pl;
- while (Pl -> Pnext) Pl = Pl -> Pnext;
- Pl -> Pnext = CopyPolygonList(PObj2 -> U.Pl); /* Obj2 polys. */
- }
- }
-
- AttrSetObjectColor(PObj, GlblBoolColor); /* Default bool object color. */
-
- return PObj;
- }
-
- /*****************************************************************************
- * Perform a Boolean NEGATION of given object PObj. *
- * Negation is simply reversing the direction of the plane equation of each *
- * polygon - the simplest Boolean operation... *
- *****************************************************************************/
- IPObjectStruct *BooleanNEG(IPObjectStruct *PObj)
- {
- int i;
- IPObjectStruct *PTemp;
- IPPolygonStruct *Pl;
- IPVertexStruct *V;
-
- if ((PTemp = VerifyBooleanInput(PObj, NULL, BOOL_OPER_NEG)) != NULL)
- return PTemp;
- else {
- PTemp = CopyObject(NULL, PObj, FALSE); /* Make fresh copy of object. */
-
- /* Scans all polygons and reverse plane equation and their vetrex */
- /* list (cross prod. of consecutive edges must be in normal dir.). */
- Pl = PTemp -> U.Pl;
- while (Pl != NULL) {
- for (i = 0; i < 4; i++) Pl -> Plane[i] = (-Pl -> Plane[i]);
- IP_RST_CONVEX_POLY(Pl);
-
- /* Invert vertices normals as well. */
- V = Pl -> PVertex;
- do {
- PT_SCALE(V -> Normal, -1.0);
- V = V -> Pnext;
- }
- while (V != NULL && V != Pl -> PVertex);
-
- Pl = Pl -> Pnext;
- }
- }
-
- signal(SIGFPE, DefaultFPEHandler); /* Default FPE trapping. */
-
- AttrSetObjectColor(PTemp, BooleanOutputInterCurve ?
- GlblICrvColor :
- GlblBoolColor); /* Default bool object color. */
-
- return PTemp;
- }
-
- /*****************************************************************************
- * Combining three geometric objects, by simply concat. their polygon lists: *
- * Any object may be NULL in which only the other two are merged. *
- *****************************************************************************/
- static IPObjectStruct *BooleanCombineThreeObjs(IPObjectStruct *PObj1,
- IPObjectStruct *PObj2,
- IPObjectStruct *PObj3)
- {
- IPPolygonStruct *Pl;
-
- if (PObj1 == NULL) {
- PObj1 = PObj2;
- PObj2 = PObj3;
- PObj3 = NULL;
- }
- if (PObj2 == NULL) {
- PObj2 = PObj3;
- PObj3 = NULL;
- }
-
- if (PObj1 != NULL)
- CleanUpPolygonList(&PObj1 -> U.Pl);
- if (PObj2 != NULL)
- CleanUpPolygonList(&PObj2 -> U.Pl);
- if (PObj3 != NULL)
- CleanUpPolygonList(&PObj3 -> U.Pl);
-
- if (PObj2 != NULL) {
- /* Concat the polygons of PObj2 after the polygons of PObj1. */
- Pl = PObj1 -> U.Pl;
- while (Pl -> Pnext != NULL)
- Pl = Pl -> Pnext;
- Pl -> Pnext = PObj2 -> U.Pl; /* Concat. the polygons into one list. */
- PObj2 -> U.Pl = NULL; /* And release the second object header. */
- IPFreeObject(PObj2);
-
- if (PObj3 != NULL) {
- /* Concat the polygons of PObj3 after the polygons of PObj1/2. */
- while (Pl -> Pnext != NULL)
- Pl = Pl -> Pnext;
- Pl -> Pnext = PObj3 -> U.Pl; /* Concat. polygons into one list. */
- PObj3 -> U.Pl = NULL; /* And release the second object header. */
- IPFreeObject(PObj3);
- }
- }
-
- return PObj1;
- }
-
- /*****************************************************************************
- * If required (I.e. Object "COPLANAR" is set to TRUE) search all coplanar *
- * polygons in PObj1/2 and invoke the necessary two dimenstional Boolean. *
- *****************************************************************************/
- static IPObjectStruct *BooleanCoplanar(IPObjectStruct *PObj1,
- IPObjectStruct *PObj2, int BoolOper)
- {
- int HandleCoplanar;
- MatrixType RotMat;
- IPObjectStruct *PObj,
- *PCoplanar = GetObject("COPLANAR");
- IPPolygonStruct *Pl, *PlTmp, *Pl1, *Pl2, *Pl1XY, *Pl2XY, *Pl1XYR, *Pl2XYR,
- *PlOut = NULL;
-
- if (PCoplanar == NULL || !IP_IS_NUM_OBJ(PCoplanar)) {
- WndwInputWindowPutStr("No numeric object name COPLANAR is defined");
- HandleCoplanar = FALSE;
- }
- else
- HandleCoplanar = (int) (PCoplanar -> U.R);
-
- if (!HandleCoplanar)
- return NULL;
-
- for ( Pl1 = PObj1 -> U.Pl; Pl1 != NULL; Pl1 = Pl1 -> Pnext) {
- for ( Pl2 = PObj2 -> U.Pl; Pl2 != NULL; Pl2 = Pl2 -> Pnext) {
- RealType
- *Plane1 = Pl1 -> Plane,
- *Plane2 = Pl2 -> Plane;
- int Shared = APX_EQ(Plane1[0], Plane2[0]) &&
- APX_EQ(Plane1[1], Plane2[1]) &&
- APX_EQ(Plane1[2], Plane2[2]) &&
- APX_EQ(Plane1[3], Plane2[3]),
- AntiShared = APX_EQ(Plane1[0], -Plane2[0]) &&
- APX_EQ(Plane1[1], -Plane2[1]) &&
- APX_EQ(Plane1[2], -Plane2[2]) &&
- APX_EQ(Plane1[3], -Plane2[3]);
-
- if (!Shared && !AntiShared) {
- /* The two polygons are not coplanar, do not intersect. */
- continue;
- }
-
- if (Shared) {
- switch (BoolOper) {
- case BOOL_OPER_AND:
- case BOOL_OPER_OR:
- case BOOL_OPER_SUB:
- GenRotateMatrix(RotMat, Pl1 -> Plane);
- Pl1XY = ComputeRotatedPolys(Pl1, TRUE, RotMat);
- Pl2XY = ComputeRotatedPolys(Pl2, TRUE, RotMat);
- /* Find the inverse matrix. */
- if (!MatInverseMatrix(RotMat, RotMat))
- IritFatalError("BooleanCoplanar: Inverse matrix does not exists");
-
- if ((Pl = Boolean2D(Pl1XY, Pl2XY, BoolOper)) != NULL) {
- Pl = ComputeRotatedPolys(Pl, FALSE, RotMat);
- for (PlTmp = Pl;
- PlTmp -> Pnext != NULL;
- PlTmp = PlTmp -> Pnext);
- PlTmp -> Pnext = PlOut;
- PlOut = Pl;
- }
-
- IPFreePolygonList(Pl1XY);
- IPFreePolygonList(Pl2XY);
- break;
-
- default:
- IritFatalError("BooleanCoplanar: Unsupported Boolean operation BooleanCoplanar");
- return NULL;
- }
- }
- else if (AntiShared) {
- switch (BoolOper) {
- case BOOL_OPER_AND:
- case BOOL_OPER_SUB:
- WndwInputWindowPutStr("Antishared coplanar polygons are ignored.");
- break;
-
- case BOOL_OPER_OR:
- GenRotateMatrix(RotMat, Pl1 -> Plane);
- Pl1XY = ComputeRotatedPolys(Pl1, TRUE, RotMat);
- Pl1XYR = CopyPolygonList(Pl1XY);
- IritPrsrReverseVrtxList(Pl1XYR);
- Pl2XY = ComputeRotatedPolys(Pl2, TRUE, RotMat);
- Pl2XYR = CopyPolygonList(Pl2XY);
- IritPrsrReverseVrtxList(Pl2XYR);
-
- if (!MatInverseMatrix(RotMat, RotMat))
- IritFatalError("BooleanCoplanar: Inverse matrix does not exists");
-
- if ((Pl = Boolean2D(Pl1XY, Pl2XYR, BOOL_OPER_SUB)) != NULL) {
- Pl = ComputeRotatedPolys(Pl, FALSE, RotMat);
- for (PlTmp = Pl;
- PlTmp -> Pnext != NULL;
- PlTmp = PlTmp -> Pnext);
- PlTmp -> Pnext = PlOut;
- PlOut = Pl;
- }
- if ((Pl = Boolean2D(Pl2XY, Pl1XYR, BOOL_OPER_SUB)) != NULL) {
- Pl = ComputeRotatedPolys(Pl, FALSE, RotMat);
- for (PlTmp = Pl;
- PlTmp -> Pnext != NULL;
- PlTmp = PlTmp -> Pnext);
- PlTmp -> Pnext = PlOut;
- PlOut = Pl;
- }
-
- IPFreePolygonList(Pl1XY);
- IPFreePolygonList(Pl2XY);
- IPFreePolygonList(Pl1XYR);
- IPFreePolygonList(Pl2XYR);
- break;
-
- default:
- IritFatalError("BooleanCoplanar: Unsupported Boolean operation BooleanCoplanar");
- return NULL;
- }
- }
- }
- }
-
- PObj = IPAllocObject("", IP_OBJ_POLY, NULL);
- PObj -> U.Pl = PlOut;
- return PObj;
- }
-
- /*****************************************************************************
- * Routine to optionally copy (if CpolyOnePl) a single polygon and rotate *
- * according to the rotation matrix provided. If, however, CopyOnePl is False *
- * all polygons in list are converted. *
- *****************************************************************************/
- static IPPolygonStruct *ComputeRotatedPolys(IPPolygonStruct *Pl, int CopyOnePl,
- MatrixType RotMat)
- {
- IPVertexStruct *V, *VHead;
- IPPolygonStruct *PlNext, *PlTemp,
- *PlOut = NULL;
-
- while (Pl != NULL) {
- PlNext = Pl -> Pnext;
-
- if (CopyOnePl) {
- PlTemp = IPAllocPolygon(Pl -> Count, Pl -> Tags,
- CopyVertexList(Pl -> PVertex), NULL);
- PLANE_COPY(PlTemp ->Plane, Pl -> Plane);
- Pl = PlTemp;
- }
-
- V = VHead = Pl -> PVertex;
- do { /* Transform the polygon itself. */
- PointType PTemp;
-
- PT_ADD(PTemp, V -> Coord, V -> Normal);
-
- MatMultVecby4by4(V -> Coord, V -> Coord, RotMat);
-
- MatMultVecby4by4(PTemp, PTemp, RotMat); /* Update normal. */
- PT_SUB(V -> Normal, PTemp, V -> Coord);
- PT_NORMALIZE(V -> Normal);
-
- V = V -> Pnext;
- }
- while (V != NULL && V != VHead);
-
- Pl -> Pnext = PlOut;
- PlOut = Pl;
-
- if (CopyOnePl)
- break;
- else
- Pl = PlNext;
- }
-
- return PlOut;
- }
-
- /*****************************************************************************
- * Routine that is called from the floating point package in case of fatal *
- * floating point error. Print error message, and quit the Boolean module. *
- *****************************************************************************/
- static void BooleanFPE(int Type)
- {
- char Line[LINE_LEN];
-
- sprintf(Line, "Floating point error %d.", Type);
- WndwInputWindowPutStr(Line);
-
- FatalErrorType = FTL_BOOL_FPE;
-
- longjmp(LclLongJumpBuffer, 1);
- }
-
- /*****************************************************************************
- * Routine to select the output kind needed. Output of a Boolean operator *
- * can be the real Boolean result model, or the intersection curves only. *
- *****************************************************************************/
- static void SetBooleanOutputKind(void)
- {
- IPObjectStruct *PObj = GetObject("INTERCRV");
-
- if (PObj == NULL || !IP_IS_NUM_OBJ(PObj)) {
- WndwInputWindowPutStr("No numeric object name INTERCRV is defined");
- BooleanOutputInterCurve = FALSE;
- }
- else
- BooleanOutputInterCurve = !APX_EQ(PObj -> U.R, 0.0);
- }
-
- /*****************************************************************************
- * Routine that is called by the bool-low module in fatal cases errors. *
- * Will print error message and long jump using LclLongJumpBuffer. *
- *****************************************************************************/
- void FatalBooleanError(int ErrorType)
- {
- char s[LINE_LEN_LONG];
-
- FatalErrorType = ErrorType;
-
- switch (ErrorType) {
- case FTL_BOOL_NO_INTER:
- /* If operation is union (OR), then if no intersection we assume */
- /* the objects have no common volume and simply combine them! */
- if (BooleanOperation != BOOL_OPER_OR) {
- sprintf(s, "Boolean: %s",
- "Objects do not intersect - Empty object result");
- WndwInputWindowPutStr(s);
- }
- break;
- default:
- sprintf(s, "Boolean: Undefined Fatal Error (%d !?)", ErrorType);
- WndwInputWindowPutStr(s);
- break;
- }
-
- longjmp(LclLongJumpBuffer, 1);
- }
-
- #ifdef DEBUG2
-
- /*****************************************************************************
- * Print the content of the given polygon, to standard output. *
- *****************************************************************************/
- static void PrintVrtxList(IPVertexStruct *V)
- {
- IPVertexStruct
- *VHead = V;
-
- do {
- printf("[NORMAL %8lf %8lf %8lf] %12lf %12lf %12lf",
- V -> Normal[0], V -> Normal[1], V -> Normal[2],
- V -> Coord[0], V -> Coord[1], V -> Coord[2]);
- if (IP_IS_INTERNAL_EDGE(V))
- printf(" (Internal)\n");
- else
- printf("\n");
- V = V -> Pnext;
- }
- while (V!= NULL && V != VHead);
- }
-
- #endif /* DEBUG2 */
-